package com.czk.bootlettuce.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

/**
 * 6.1.1.简介
 * 现代计算机用二进制（位） 作为信息的基础单位， 1个字节等于8位， 例如“abc”字符串是由3个字节组成， 但实际在计算机存储时将其用二进制表示，
 * “abc”分别对应的ASCII码分别是97、 98、 99， 对应的二进制分别是01100001、 01100010和01100011，如下图
 * <p>
 * 合理地使用操作位能够有效地提高内存使用率和开发效率。
 * Redis提供了Bitmaps这个“数据类型”可以实现对位的操作：
 * （1）Bitmaps本身不是一种数据类型， 实际上它就是字符串（key-value） ， 但是它可以对字符串的位进行操作。
 * （2）Bitmaps单独提供了一套命令， 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组，
 * 数组的每个单元只能存储0和1， 数组的下标在Bitmaps中叫做偏移量。
 *
 * @Author:ChenZhangKun
 * @Date: 2021/12/5 16:10
 */
@RequestMapping("/bitmap")
@RestController
public class BitMapsController {
    private static final String key = "unique:users:20211205";
    private static final String key1 = "unique:users:20211206";
    private static final String TODAY_PREDIX = "unique:users:";
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 添加用户访问
     * 每个用户访问就把对应的二进制下标位置设为1
     *
     * @param uid
     * @return
     */
    @GetMapping("/add/{uid}")
    public String add(@PathVariable Integer uid) {
        Boolean aBoolean = redisTemplate.opsForValue().setBit(key1, uid, true);
        return Boolean.TRUE.equals(aBoolean) ? "失败" : "成功";
    }

    /**
     * 每日登录
     *
     * @param id
     * @return
     */
    @GetMapping("/login/{id}")
    public String login(@PathVariable Integer id) {
        String format = formatter.format(LocalDate.now());
        boolean isSuccess = Boolean.TRUE.equals(redisTemplate.opsForValue().setBit(TODAY_PREDIX + format, id, true));
        return isSuccess ? "成功" : "失败";
    }

    /**
     * 查看用户是否访问
     *
     * @param uid
     * @return
     */
    @GetMapping("/offset/{uid}")
    public String getOffset(@PathVariable Integer uid) {
        Boolean bit = redisTemplate.opsForValue().getBit(key, uid);
        if (bit) {
            return "存在";
        } else {
            return "不存在";
        }
    }

    /**
     * 查看访问数量
     *
     * @return
     */
    @GetMapping("/bitCount")
    public Long bitCount() {
        Object execute = redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
        return (Long) execute;
    }

    @GetMapping("/countUser")
    public void countUser() {
        // 生成count-user-and 键，统计两天都登录的用户
        redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitOp(RedisStringCommands.BitOperation.AND, "count-user-and".getBytes(),
                key.getBytes(), key1.getBytes()));
        // 生成count-user-or 键，统计两天内登陆过的用户
        redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitOp(RedisStringCommands.BitOperation.OR, "count-user-or".getBytes(),
                key.getBytes(), key1.getBytes(), (TODAY_PREDIX+formatter.format(LocalDate.now())).getBytes()));

        System.out.println("两天都登陆的用户个数 :" + redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("count-user-and".getBytes())));
        System.out.println("两天内登陆过的用户个数 :" + redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("count-user-or".getBytes())));
    }
}
